/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel;

import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.jar.JarEntry;

import org.zhiqim.kernel.annotation.AnGlobal;
import org.zhiqim.kernel.annotation.AnNew;
import org.zhiqim.kernel.config.Config;
import org.zhiqim.kernel.config.Group;
import org.zhiqim.kernel.config.Item;
import org.zhiqim.kernel.constants.ZhiqimConstants;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.model.FilterHandler;
import org.zhiqim.kernel.model.Filters;
import org.zhiqim.kernel.model.results.RE;
import org.zhiqim.kernel.service.Service;
import org.zhiqim.kernel.util.Annotations;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Closes;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Jars;
import org.zhiqim.kernel.util.Resources;
import org.zhiqim.kernel.util.Systems;
import org.zhiqim.kernel.util.Validates;

/**
 * 知启蒙工程入口类<br><br>
 * 
 * 一、判断是否是命令，如果是转到ZhiCommand处理
 * 二、启动工程，调用startup方法
 * 1.检查Java版本
 * 2.加载配置文件信息<br>
 * 3.注册监听<br>
 * 4.加载ClassPath和ClassLoader<br>
 * 5.加载工程服务<br>
 * 6.生成工程注册Shutdown锚
 * 
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
public final class Zhiqim implements ZhiqimConstants
{
    private static final Log log = LogFactory.getLog(Zhiqim.class);
    private static ZhiqimClassLoader zClassLoader;
    
    /** 不允许实例化 */
    private Zhiqim(){}
    
    /** 工程启动入口 */
    public static void main(String[] args)
    {
        if (args.length == 0)
        {//无参数，开发环境启动
            startup(true);
        }
        else if (args.length == 1 || args.length == 2)
        {//有1-2参数
            if (Z_START_A.equals(args[0]))
            {//生产环境启动
                startup(false);
            }
            else
            {//其他6种命令
                ZhiqimCommand.command(args);
            }
        }
        else
        {//不支持的参数
            System.out.println("不支持的参数，请使用-h查看支持的参数");
        }
    }
    
    /** 工程启动 */
    private static void startup(boolean development)
    {
        //1.检查版本
        if (!chkJavaVersion())
        {
            System.exit(1);
            return;
        }
        
        //2.加载配置文件
        if (!loadConfig())
        {
            System.exit(1);
            return;
        }
        
        //3.注册监听端口
        if (!registerMonitor())
        {
            System.exit(1);
            return;
        }
        
        //注册监听端口成功之后表示开始初始化
        log.info("系统[%s]-版本[%s]初始化开始...\r\n", Z.conf().getName(), Z.conf().getVersion());
        
        //4.创建类加载器
        if (!createClassLoader(development))
        {
            System.exit(1);
            return;
        }
        
        //5.创建服务
        if (!createServices())
        {
            System.exit(1);
            return;
        }
        
        //6.注册工程关闭锚
        Runtime.getRuntime().addShutdownHook(new ZhiqimHook());
        
        //初始化完成打印日志
        log.info("系统[%s]-版本[%s]初始化完成!!!\r\n", Z.conf().getName(), Z.conf().getVersion());
    }
    
    /** 检查JAVA版本 */
    public static boolean chkJavaVersion()
    {
        String vers = Systems.getJavaVersion();
        if (vers.compareTo("1.7") < 0)
        {
            System.out.println("JDK版本不能低于[1.7]");
            System.out.println("java.vm.info:  " + Systems.getVmInfo());
            System.out.println("java.vm.name:  " + Systems.getVmName());
            System.out.println("java.vm.version:  " + Systems.getVmVersion());
            return false;
        }
        
        return true;
    }
    
    /** 初始化配置文件 */
    private static boolean loadConfig()
    {
      //先检查xml再检查ini
        String path = null;
        if (Files.exists(Z_CONF_ZHIQIM_XML))
            path = Z_CONF_ZHIQIM_XML;
        else if (Files.exists(Z_CONF_ZHIQIM_INI))
            path = Z_CONF_ZHIQIM_INI;
        
        if (path == null)
        {
            log.error("配置文件[%s]和[%s]都不存在，请按手册配置目录结构", Z_CONF_ZHIQIM_XML, Z_CONF_ZHIQIM_INI);
            return false;
        }
        
        Config config = new Config(Z_NAME, path);
        
        try
        {
            //1.加载根配置文件
            config.load();
            
            //1.1检查[boot]下name,version,port三个字段是否必须
            if (!config.hasGroup(Z_BOOT) || !config.hasItem(Z_BOOT, Z_ITEM_NAME) || !config.hasItem(Z_BOOT, Z_ITEM_VERSION) || !config.hasItem(Z_BOOT, Z_ITEM_PORT))
            {
                log.error("配置文件[boot]组中name,version,port必须的，请配置好再启动");
                return false;
            }
            
            //1.2把配置加到全局表中
            String result = Z.conf().add(config);
            if (result != null)
            {
                log.error("根配置文件[%s]在这里[%s]有配置错误", path, result);
                return false;
            }
            
        }
        catch(Exception e)
        {
            log.error("初始化配置文件[%s]时异常", e, path);
            return false;
        }
        
        //2.判断是否需要加载其他的配置文件
        if (config.hasGroup(Z_CONFIG))
        {
            Collection<Item> itemList = config.getItemList(Z_CONFIG);
            for (Item item : itemList)
            {//2.1循环加载
                try
                {
                    Config conf = new Config(item.getKey(), item.getString()).load();
                    
                    //2.2加到全局表中，失败退出
                    String result = Z.conf().add(conf);
                    if (result != null)
                    {
                        log.error("配置文件[%s]存在和别的配置文件相同的配置组[%s]", item.getString(), result);
                        return false;
                    }
                }
                catch(Exception e)
                {
                    log.error("初始化配置文件[%s]时异常", e, item.getString());
                    return false;
                }
            }
        }
        
        return true;
    }
    
    /** 初始化监听 */
    private static boolean registerMonitor()
    {
        //启动监视命令线程
        int port = Z.conf().getPort();
        if (port < 1 || port > 65535)
        {
            log.error("配置文件[boot.port]不是正确的端口");
            return false;
        }
        
        return new ZhiqimMonitor(port).open();
    }
    
    /** 初始化框架级ClassLoader */
    private static boolean createClassLoader(boolean development)
    {
        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        if (parent == null)
            parent = Zhiqim.class.getClassLoader();

        if (parent == null)
            parent = ClassLoader.getSystemClassLoader();
        
        //创建框架级ClassLoader，并设置到全局和当前上下文中
        zClassLoader = new ZhiqimClassLoader(parent, development);
        Thread.currentThread().setContextClassLoader(zClassLoader);
        
        try
        {
            if (!zClassLoader.open())
            {
                log.error("初始化类加载器时失败");
                Closes.closeIgnoreException(zClassLoader);
                return false;
            }
            
            return true;
        }
        catch(Exception e)
        {
            log.error("初始化类加载器时异常", e);
            Closes.closeIgnoreException(zClassLoader);
            return false;
        }
    }
    
    /** 初始化服务列表 */
    private static boolean createServices()
    {
        if (!Z.conf().hasGroup(Z_SERVICE))
            return true;

        try
        {
            Group group = Z.conf().group(Z_SERVICE);
            for (Item item : group.list())
            {
                if (!openService(item.getKey()))
                    return false;
            }
            
            return true;
        }
        catch (Exception e)
        {
            log.error("初始化创建服务时异常，退出工程", e);
            return false;
        }
    }
    
    /** 创建指定的服务 */
    static boolean openService(String serviceId) throws Exception
    {
        Item item = Z.conf().item(Z_SERVICE, serviceId);
        if (item == null)
        {
            log.fatal("服务[%s]配置不存在");
            return false;
        }
        
        String serviceClassName = item.getString();
        String serviceLoaderValue = Z.conf().getString(Z_SERVICE_LOADER, item.getKey());
        
        if (Validates.isEmpty(serviceLoaderValue))
        {//无需创建classLoader
            return loadService(serviceId, serviceClassName, zClassLoader);
        }
        else
        {//需要创建classLoader
            ZhiqimClassServiceLoader serviceLoader = new ZhiqimClassServiceLoader(zClassLoader, serviceId, serviceLoaderValue);
            if (!serviceLoader.open())
            {//打开加载器失败
                serviceLoader.close();
                return false;
            }
            
            try
            {
                Thread.currentThread().setContextClassLoader(serviceLoader);
                return loadService(serviceId, serviceClassName, serviceLoader);
            }
            finally
            {
                Thread.currentThread().setContextClassLoader(zClassLoader);
            }
        }
    }
    
    /** 加载服务 */
    private static boolean loadService(String serviceId, String serviceClassName, ClassLoader serviceLoader) throws Exception
    {
        Object obj = Z.cls().newInstance(serviceClassName);
        if (obj == null)
        {
            log.fatal("服务[%s]未找到该类", serviceClassName);
            return false;
        }
        
        if (!(obj instanceof Service))
        {
            log.fatal("服务[%s]未实现Service接口，退出工程", serviceClassName);
            return false;
        }
        
        Service service = (Service)obj;
        service.setId(serviceId);
        service.setClassLoader(serviceLoader);
        //先放置到全局服务中，后创建，有利于服务运行中即可从全局中获取服务
        Z.serv().add(service);
        if (!service.create())
        {
            log.fatal("服务[%s]创建失败", serviceClassName);
            return false;
        }
        
        return true;
    }
    
    /** 关闭服务 */
    static boolean closeService(String serviceId)
    {
        Service service = Z.serv().get(serviceId);
        if (service == null)
        {
            log.error("服务["+serviceId+"]不存在或未在系统中加载");
            return false;
        }
        
        try
        {
            ClassLoader classLoader = service.getClassLoader();
            if (!(classLoader instanceof ZhiqimClassServiceLoader))
            {
                service.destroy();
            }
            else
            {
                ClassLoader currLoader = Thread.currentThread().getContextClassLoader();
                try
                {
                    Thread.currentThread().setContextClassLoader(classLoader);
                    service.destroy();
                    ((ZhiqimClassServiceLoader) classLoader).close();
                }
                finally
                {
                    Thread.currentThread().setContextClassLoader(currLoader);
                }
            }
            
            Z.serv().remove(serviceId);
            System.gc();

            log.info("服务["+serviceId+"]关闭成功");
            return true;
        }
        catch (Exception e)
        {
            log.error("服务["+serviceId+"]关闭异常", e);
            return false;
        }
    }
    
    /** 加载类短别称，公开的原因是单元测试时方便加载./bin目录的别名 */
    public static boolean loadClassAliasName(List<String> pathList)
    {
        FilterHandler handler = new FilterHandler() 
        {
            @Override
            public boolean handle(Object obj) throws Exception
            {
                String className = null;
                Object[] objs = (Object[])obj;
                if (objs[0] instanceof File)
                {
                    File classPath = (File)objs[0];
                    File classFile = (File)objs[1];
                    className = Files.getClassName(classPath, classFile);
                }
                else
                {
                    JarEntry jarEntry = (JarEntry)objs[1];
                    className = Jars.getClassName(jarEntry);
                }
                
                return addClassAlias(className) != null;
            }
        };
        
        return Resources.scanClassPath(Filters.CLASS, handler, pathList);
    }
    
    /**
     * 通过全称类，找到短名类注解，设置短名类名称和类结构到全局变量中
     * 
     * @param className 类全称
     */
    static String addClassAlias(String className)
    {
        RE<Class<?>> result = Classes.name(className);
        if (!result.success())
        {
            log.error("检查到类[%s]加载失败[类不存在或类的静态属性初始化失败]", result.exception(), className);
            return null;
        }
        
        Class<?> cls = result.value();
        if (cls.isAnnotationPresent(AnGlobal.class) && cls.isAnnotationPresent(AnNew.class))
        {
            log.error("[%s]不能同时定义成[AnGlobal]和[AnNew]注解，两者只允许一个", className);
            return null;
        }
        
        String alias = Annotations.getClassAlias(cls);
        if (alias == null)
            return _EMPTY_;
        
        Class<?> has = Z.cls().get(alias);
        if (has != null)
        {
            log.error("[%s]和[%s]的简称[%s]不允许相同", className, has.getName(), alias);
            return null;
        }
        
        Z.cls().add(alias, cls);
        return alias;
    }
    
    /** 获取框架类加载器 */
    public ZhiqimClassLoader getZhiqimClassLoader()
    {
        return zClassLoader;
    }
}
